#include "Helpers.h"
#include "VertexDirectory.h"
#include "Vertex.h"
#include "VertexBase.h"
//#include "TestUtils.h"
#include <map>


/*****************************************************************************/
// Algorithms class function implementation
/*****************************************************************************/
//-----------------------------------------------------------------------------
// UpdateGraphAndGroupFromCopies
// updates the given graph and group from the given copy of the group
// removes all vertices in copyGroup from the graph
// updates group to contain only vertices in copyGroup
//-----------------------------------------------------------------------------
void Algorithms::UpdateGraphAndGroupFromCopies(Graph& graph, Group& group,Group& copyGroup)
{
	graph.AddGroup(group);
	graph.RemoveGroupVerticesFromGraph(&copyGroup);
	group.Clone(copyGroup);
}

//-----------------------------------------------------------------------------
// ValidateVertexDirectoryState
// Is intended to run after an algorithm is executed.
// This function validates the state of the VertexDirectory, in relation to the
// graph and one group (the group that has been changed) - should NOT be used to
// validate the data structures' state when working on a chain (use 
// ValidateVertexDirectoryState(Graph *graph, list<Group*> groups, set<int> groupNames,
// VertexDirectory* dir) instead).
// validates that all
// the vertices that appear in the graph are marked as "VertexInGraph" in the 
// vertex instance. 
// Validates that all the vertices that appear in the group, point to the gourp
// as their current container.
//-----------------------------------------------------------------------------
bool Algorithms::ValidateVertexDirectoryState(Graph *graph, Group *group, VertexDirectory* dir)
{
	map<vector<bool>, Vertex*>* vertices;
	if (dir == NULL)
	{
		vertices = VertexDirectory::GetStaticCopyVertices();
	}
	else
	{
		vertices = dir->GetWorkingCopyVertices();
	}
	map<vector<bool>, Vertex*>::iterator it;
	for (it = vertices->begin(); it != vertices->end(); it++)
	{
		if (graph->IsVertexInGraph((*it).second) == true)
		{	// can't check vertex container because the vertex points at a RankSet and not the graph
			if ((*it).second->IsInGraph() == false)
			{
				Debug::PrintDebugMsg("A vertex in the graph doesn't appear in the graph in VertexDirectory:" + (*it).second->PrintName());
				return false;
			}
		}
		else
		{	// vertex is in the group: shallow check that the pointers point to the same address in the memory
				if ((void*)((*it).second)->GetContainer() != (void*)(group))
			{
				Debug::PrintDebugMsg("A vertex in the group doesn't point to the group as its container:" + (*it).second->PrintName());
				return false;
			}		
		}
	}
	return true;
}

//-----------------------------------------------------------------------------
// ValidateVertexDirectoryState
// Is intended to run after an algorithm is executed.
// This function validates the state of the VertexDirectory, in relation to the
// given chain
// validates that all the vertices that appear in the graph are marked as 
// "VertexInGraph" in the vertex instance. 
// Validates that all the vertices that appear in the groups, point to a group
// in the list.
//-----------------------------------------------------------------------------
bool Algorithms::ValidateVertexDirectoryState(Graph *graph, list<Group*> groups, set<int> groupNames, VertexDirectory* dir)
{
	map<vector<bool>, Vertex*>* vertices;
	bool retVal = true;
	if (dir == NULL)
	{
		vertices = VertexDirectory::GetStaticCopyVertices();
	}
	else
	{
		vertices = dir->GetWorkingCopyVertices();
	}
	map<vector<bool>, Vertex*>::iterator it;
	for (it = vertices->begin(); it != vertices->end(); it++)
	{
		if ((*it).second->IsReplacement() && (*it).second->Interchangeable())
		{
			Debug::PrintDebugMsg((*it).second->PrintName() + " is both interchangeable and replacement");
			retVal = false;
		}

		if (graph->IsVertexInGraph((*it).second) == true)
		{	// can't check vertex container because the vertex points at a RankSet and not the graph
			if ((*it).second->IsInGraph() == false)
			{
				Debug::PrintDebugMsg("A vertex in the graph doesn't appear in the graph in VertexDirectory:" + (*it).second->PrintName());
				VertexContainer *container = it->second->GetContainer();
				if (container == NULL)
				{
					Debug::PrintDebugMsg("Container is null");
				} else
				{
					Group *group = dynamic_cast<Group*>(container);
					if (group != NULL)
					{
						string str = "Container is a group";
						str += "Group name: " + group->GetGroupName();
						Debug::PrintDebugMsg(str);
						if (groupNames.find(group->GetGroupName())!= groupNames.end())
						{
							Debug::PrintDebugMsg("Group is actually in chain");
						}
					} 
					else
					{
						RankSet *rankSet = dynamic_cast<RankSet*>(container);
						if (rankSet != NULL)
						{
							Debug::PrintDebugMsg("Container is a rankset");
						} 
						else
						{
							TempVertexContainer *temp = dynamic_cast<TempVertexContainer*>(container);
							if (temp != NULL)
							{
								Debug::PrintDebugMsg("Vertex is in temp container");
							} 
							else
							{
								Debug::PrintDebugMsg("Vertex container is no known container!");
							}
						}
					}
				}
				retVal =  false;
			}
		}
		else
		{	// vertex is in the group: shallow check that the pointers point to the same address in the memory
			Group *vertexGroup = dynamic_cast<Group*>((it->second)->GetContainer());
			if (vertexGroup == NULL)
			{
				Debug::PrintDebugMsg("A vertex in the group points to something that ins't a group as its container:"  + (*it).second->PrintName());
				retVal =  false;
			} else 
			{
				if (groupNames.find(vertexGroup->GetGroupName()) == groupNames.end())
				{
					Debug::PrintDebugMsg("A vertex in the group doesn't point to any group as its container:"  + (*it).second->PrintName());
					retVal = false;
				}
				if (vertexGroup->IsVertexInGroup(it->second->Name()) == false)
				{
					Debug::PrintDebugMsg("A vertex points to a group it doesn't belong to:"  + (*it).second->PrintName());
					retVal = false;
				}
				if (it->second->IsInGraph() == true)
				{
					Debug::PrintDebugMsg("A vertex in the group (is not in graph!) thinks it is in graph" + (*it).second->PrintName());
					retVal = false;
				}	
			}
		}
	}

	list<Group*>::iterator groupIt;
	for (groupIt = groups.begin(); groupIt != groups.end(); groupIt++)
	{
		Group *currentGroup = *groupIt;
		list<Vertex*>::iterator vertexIt;
		for (vertexIt=currentGroup->Iterator(); currentGroup->End(vertexIt) == false; vertexIt++)
		{
			if ((*vertexIt)->IsInGraph() == true)
			{
				Debug::PrintDebugMsg("A vertex in the group thinks it is in graph" + (*vertexIt)->PrintName());
				retVal =  false;
			}
		}
		if (!Algorithms::ValidateGroupIsIndependent(currentGroup))
		{
			cout << "Group is not independent: " <<  currentGroup->GetGroupName() << endl;
			retVal = false;
		}
	}
	return retVal;
}

//---------------------------------------------------------------------------------
// ValidateGroupIsIndependent: validates there aren't any neighbours in the group
//---------------------------------------------------------------------------------
bool Algorithms::ValidateGroupIsIndependent(Group* group)
{
	// The group is independent iff it is legal to add each vertex to the group
	list<Vertex*>::iterator it;
	for (it = group->Iterator(); (group->End(it) == false); it++)
	{
		if ((*it)->HasNeighbourInGroup(group))
		{
			return false;
		}
	}
	return true;
}

//---------------------------------------------------------------------------------
// ValidateGroupsAreDisjoint - returns true iff there is no vertex that is a member 
// of both groups (groupA, groupB)
//---------------------------------------------------------------------------------
bool Algorithms::ValidateGroupsAreDisjoint(Group *groupA, Group *groupB)
{
	if ((groupA == NULL) || (groupB == NULL))
	{
		Debug::PrintDebugMsg("One of the groups passed to TestUtils::ValidateGroupsAreDisjoint is NULL");
		return false;
	}
	list<Vertex*>::iterator it;
	for (it = groupA->Iterator(); (groupA->End(it) == false); it++)
	{
		if (groupB->IsVertexInGroup((*it)->Name()))
		{
			string str = "Group #";
			str += groupA->GetGroupName();
			str += " and group #";
			str += groupB->GetGroupName();
			str += " aren't disjoint: Both contain vertex ";
			str += (*it)->PrintName();
			
			Debug::PrintDebugMsg(str);

			return false;
		}
	}
	return true;
}

//---------------------------------------------------------------------------------
// ValidateGroupInChainIsDisjoint - checks that all the groups in the chain are
// disjoint in  relation to the given group
//---------------------------------------------------------------------------------
bool Algorithms::ValidateGroupInChainIsDisjoint(Chain* chain, Group* group)
{
	if ((group == NULL) || (chain == NULL))
	{
		Debug::PrintDebugMsg("The chain or the group passed to TestUtils::ValidateGroupInChainIsDisjoint is NULL");
		return false;
	}
	list<Group*>::iterator it;
	for (it = chain->Begin(); it != chain->End(); it++)
	{
		if ((*it)->GetGroupName() == group->GetGroupName())
		{	// iterator now points to the given group. skip to the next one
			continue;
		}
		if (ValidateGroupsAreDisjoint(group, (*it)) == false)
		{	//groups aren't disjoint
			return false;
		}
	}
	return true;
}

//---------------------------------------------------------------------------------
// ValidateAllGroupsAreIndependent - checks that all the groups in the chain are
// pairwise disjoint
//---------------------------------------------------------------------------------
bool Algorithms::ValidateAllGroupsAreIndependent(Chain *chain)
{	
	list<Group*> groups = chain->ChainGroups();
	list<Group*>::iterator it;
	bool isOk = true;
	for (it = groups.begin(); it != groups.end(); it++)
	{
		if (!Algorithms::ValidateGroupIsIndependent(*it))
		{
			isOk = false;
			string str = "Group " + (*it)->GetGroupName();
			str +=	" is not indepentdent";
			Debug::PrintDebugMsg(str);
		}
	}
	return isOk;
}


//*****************************************************************************
// Debug Class
// Implementation of the Debug functions
//*****************************************************************************

//-----------------------------------------------------------------------------
// PrintDebugMsg
// writes the given message to the debug stream. for each message also writes the
// current date and time.
//
// @param msg
//		the message to write to the stream.
//-----------------------------------------------------------------------------
/*static*/ void Debug::PrintDebugMsg(string msg)
{
	if(s_DebugOn)
		Logger::LogMessage(msg, false);
}

/*****************************************************************************/
// Logger
// Implementation of the Logger functions
/*****************************************************************************/

//initializing static fields:
std::ostream Logger::s_OutStream(std::cout.rdbuf());

//----------------------------------------------------------------------------
// LogMessage
// writes the given message to the stdout stream and to the log file.
// for each message also writes the current date and time.
//
// @param msg
//		the message to write to the streams.
// @isToStdout
//		if true (default) - the msg will be written to the log file and to stdout
//		if false - the msg will be written to the log file only.
//----------------------------------------------------------------------------
/*static*/ void Logger::LogMessage(string msg, bool isToStdout)
{
	string currTime = GetCurrentTime(false);
	
	if(isToStdout)
		cout << currTime << " :: " << msg << endl;
	
	s_OutStream << currTime << " :: " << msg << endl;
	s_OutStream.flush();
}

//-----------------------------------------------------------------------------
// ChangeLogStream
// changes the log stream from a file stream to stdout (and vice versa)
// according to the given parameter.
//
// @param isStdout
//		if true changes the stream to be stdout
//		if false changes the stream to be a file.
//		the name of the file that is created is in the format: 
//		dd-mm_hh-mm_graphcoloring.debug
//-----------------------------------------------------------------------------
/*static*/ void Logger::ChangeLogStream(bool isStdout)
{
	if(isStdout)
	{
		s_OutStream.rdbuf(std::cout.rdbuf());
	}
	else
	{
		string fileName = GetCurrentTime(true);
		fileName += "_graphcoloring.debug";
		std::ofstream *fileStream = new std::ofstream(fileName.c_str());
		s_OutStream.rdbuf(fileStream->rdbuf());
	}
}

/*****************************************************************************/
// General functions implementation
/*****************************************************************************/
//-----------------------------------------------------------------------------
// GetCurrentTime
// returns a string with the current date and time.
// the string is in the format:
// dd-mm_hh-mm if isForFileName is true
// or in the format:
// dd.mm hh:mm if isForFileName is false.
//
// @param isForFileName
//		sets the format type of the returned string
//
// @return string
//		the string with the current time and date.
//-----------------------------------------------------------------------------
string GetCurrentTime(bool isForFileName)
{
	time_t rawtime;
	struct tm * timeinfo = new struct tm;
	char buffer [80];
	time ( &rawtime );	
	localtime_s (timeinfo, &rawtime );
	if(isForFileName)
		strftime (buffer,80,"%d-%m_%H-%M",timeinfo);
	else
		strftime (buffer,80,"%d.%m %H:%M",timeinfo);
	string time = buffer;
	return time;		
}